package model;

import java.util.ArrayList;

/**
 * This class returns all types of slot configuration pertaining to a position in the grid. 
 *
 */
public class Slot
{

    private int rowIndex;
    private int columnIndex;
    private static Slot[][] slots;
       
    static 
    {
        Slot.slots = new Slot[LadderGame.GRID_ROW_SIZE][];

        for (int i = 0; i < Slot.slots.length; i++)
        {
            int columnSize = 2 * (slots.length - i) - 1;
            Slot.slots[i] = new Slot[columnSize];


            for (int j = 0; j < columnSize; j++)
            {
                Slot.slots[i][j] = new Slot(i, j);
            }
        }
    }

    /**
     * Returns a random slot from the bottom of the grid.
     * 
     * @return a random slot
     */
    public static Slot getRandomBottomSlot()
    {
        int columnIndex = (int)(Math.random() * 20) % LadderGame.GRID_COLUMN_SIZE;
        return Slot.get(0, columnIndex);
    }
        
    /**
     * Returns the Slot at Position(rowIndex, columnIndex)
     * 
     * @param rowIndex
     * @param columnIndex
     * @return
     * @throws IndexOutOfBoundsException
     */
    public static Slot get(int rowIndex, int columnIndex) throws IndexOutOfBoundsException
    {
        return Slot.slots[rowIndex][columnIndex];
    }

    private Slot(int rowIndex, int columnIndex) throws IndexOutOfBoundsException
    {
        if (0 <= rowIndex && rowIndex < LadderGame.GRID_ROW_SIZE 
                && 0 <= columnIndex && columnIndex < 2 * (LadderGame.GRID_ROW_SIZE - rowIndex) - 1)
        {
            this.rowIndex = rowIndex;
            this.columnIndex = columnIndex;
        }
        else
        {
            throw new IndexOutOfBoundsException("Position (" + rowIndex + "," + columnIndex + ") does not exists.");
        }
    }

    @Override
    public boolean equals(Object obj)
    {
        if (obj instanceof Slot)
        {
            Slot slot = (Slot) obj;
            return this.columnIndex == slot.columnIndex && this.rowIndex == slot.rowIndex;
        }
        else
        {
            return false;
        }
    }

    /**
     * returns All Adjacent Slots next to this one.
     * 
     * @return slots an array of slot where :
     * 
     *         slots[0] = left slot (or null if slot position does not exists)
     * 
     *         slots[1] = right slot (or null if slot position does not exists)
     *         
     *         slots[2] = upper slot (or null if slot position does not exists)
     *         
     *         slots[3] = lower slot (or null if slot position does not exists)
     *         
     *         slots[4] = upper left slot (or null if slot position does not exists)
     *         
     *         slots[5] = lower right slot (or null if slot position does not exists)
     *         
     *         slots[6] = upper right slot (or null if slot position does not exists)
     *         
     *         slots[7] = lower slot (or null if slot position does not exists)
     */
    public Slot[] getAllAdjacentSlot()
    {
        Slot[] results = new Slot[8];

        results[0] = this.getLeftSlot();
        results[1] = this.getRightSlot();
        results[2] = this.getUpperSlot();
        results[3] = this.getLowerSlot();
        results[4] = this.getUpperLeftSlot();
        results[5] = this.getLowerRightSlot();
        results[6] = this.getUpperRightSlot();
        results[7] = this.getLowerLeftSlot();
        
        return results;
    }    
    
    /**
     * returns the left and right slots adjacent to this slot.
     * 
     * @return slots an array of slot where :
     * 
     *         slots[0] = left slot (or null if slot position does not exists)
     * 
     *         slots[1] = right slot (or null if slot position does not exists)
     */
    public Slot[] getLeftAndRightAdjacentSlots()
    {
        Slot[] results = new Slot[2];

        results[0] = this.getLeftSlot();
        results[1] = this.getRightSlot();

        return results;
    }

    /**
     * returns the slot on the left and the slot above this slot.
     * 
     * @return slots an array of slot where :
     * 
     *         slots[0] = slot on the left (or null if slot position does not
     *         exists)
     * 
     *         slots[1] = upper slot (or null if slot position does not exists)
     */
    public Slot[] getLeftAndUpperAdjacentSlots()
    {
        Slot[] results = new Slot[2];

        results[0] = this.getLeftSlot();
        results[1] = this.getUpperSlot();

        return results;
    }

    /**
     * returns the slot on the right and the slot above this slot.
     * 
     * @return slots an array of slot where :
     * 
     *         slots[0] = slot on the right (or null if slot position does not
     *         exists)
     * 
     *         slots[1] = upper slot (or null if slot position does not exists)
     */
    public Slot[] getRightAndUpperAdjacentSlots()
    {
        Slot[] results = new Slot[2];

        results[0] = this.getRightSlot();
        results[1] = this.getUpperSlot();

        return results;
    }

    /**
     * returns the slots above and below this slot.
     * 
     * @return slots an array of slot where :
     * 
     *         slots[0] = upper slot (or null if slot position does not exists)
     * 
     *         slots[1] = lower slot (or null if slot position does not exists)
     */
    public Slot[] getUpperAndLowerAdjacentSlots()
    {
        Slot[] results = new Slot[2];

        results[0] = this.getUpperSlot();
        results[1] = this.getLowerSlot();

        return results;
    }

    /**
     * returns the upper left slot and lower left to this slot.
     * 
     * @return slots an array of slot where :
     * 
     *         slots[0] = upper left slot (or null if slot position does not
     *         exists)
     * 
     *         slots[1] = lower right slot (or null if slot position does not
     *         exists)
     */
    public Slot[] getUpperLeftAndLowerRightAdjacentSlots()
    {
        Slot[] results = new Slot[2];

        results[0] = this.getUpperLeftSlot();
        results[1] = this.getLowerRightSlot();

        return results;
    }

    /**
     * returns the upper right slot and lower left to this slot.
     * 
     * @return slots an array of slot where :
     * 
     *         slots[0] = upper right slot (or null if slot position does not
     *         exists)
     * 
     *         slots[1] = lower left slot (or null if slot position does not
     *         exists)
     */
    public Slot[] getUpperRightAndLowerLeftAdjacentSlots()
    {
        Slot[] results = new Slot[2];

        results[0] = this.getUpperRightSlot();
        results[1] = this.getLowerLeftSlot();

        return results;
    }

    /**
     * returns the slot on the left to this slot (or null if slot position does
     * not exists).
     * 
     * @return
     */
    public Slot getLeftSlot()
    {
        Slot leftSlot = null;
        try
        {
            leftSlot = new Slot(this.rowIndex, this.columnIndex - 1);
        }
        catch (IndexOutOfBoundsException ignored)
        {
        }
        return leftSlot;
    }

    /**
     * returns the slot on the right to this slot (or null if slot position does
     * not exists).
     * 
     * @return
     */
    public Slot getRightSlot()
    {
        Slot rightSlot = null;
        try
        {
            rightSlot = new Slot(this.rowIndex, this.columnIndex + 1);
        }
        catch (IndexOutOfBoundsException ignored)
        {
        }
        return rightSlot;
    }

    /**
     * Returns the slot on the upper left position of this slot. (or null if
     * slot position does not exists).
     * 
     * @return
     */
    public Slot getUpperLeftSlot()
    {
        Slot upperLeftSlot = null;
        try
        {
            upperLeftSlot = new Slot(this.rowIndex + 1, this.columnIndex - 2);
        }
        catch (IndexOutOfBoundsException ignored)
        {
        }
        return upperLeftSlot;
    }

    /**
     * returns the slot right above this slot (or null if slot position does not
     * exists).
     * 
     * @return
     */
    public Slot getUpperSlot()
    {
        Slot upperSlot = null;
        try
        {
            upperSlot = new Slot(this.rowIndex + 1, this.columnIndex - 1);
        }
        catch (IndexOutOfBoundsException ignored)
        {
        }
        return upperSlot;
    }

    /**
     * Returns the slot on the upper right position of this slot. (or null if
     * slot position does not exists).
     * 
     * @return
     */
    public Slot getUpperRightSlot()
    {
        Slot upperRightSlot = null;
        try
        {
            upperRightSlot = new Slot(this.rowIndex + 1, this.columnIndex);
        }
        catch (IndexOutOfBoundsException ignored)
        {
        }
        return upperRightSlot;
    }

    /**
     * Returns the slot on the lower left position of this slot. (or null if
     * slot position does not exists).
     * 
     * @return
     */
    public Slot getLowerLeftSlot()
    {
        Slot lowerLeftSlot = null;
        try
        {
            lowerLeftSlot = new Slot(this.rowIndex - 1, this.columnIndex);
        }
        catch (IndexOutOfBoundsException ignored)
        {
        }
        return lowerLeftSlot;
    }

    /**
     * Returns the slot right below this slot. (or null if slot position does
     * not exists).
     * 
     * @return
     */
    public Slot getLowerSlot()
    {
        Slot lowerSlot = null;
        try
        {
            lowerSlot = new Slot(this.rowIndex - 1, this.columnIndex + 1);
        }
        catch (IndexOutOfBoundsException ignored)
        {
        }
        return lowerSlot;
    }

    /**
     * Returns the slot on the lower right position of this slot. (or null if
     * slot position does not exists).
     * 
     * @return
     */
    public Slot getLowerRightSlot()
    {
        Slot lowerRightSlot = null;
        try
        {
            lowerRightSlot = new Slot(this.rowIndex - 1, this.columnIndex + 2);
        }
        catch (IndexOutOfBoundsException ignored)
        {
        }
        return lowerRightSlot;
    }

    /**
     * Returns the column Index as defined on the board : boardColumnIndex of A
     * = 0, boardColumnIndex of B = 1, etc.
     * 
     * ex : if rowindex = 6, columnIndex = 0 => boardColumnIndex = 6 (column G)
     * if rowindex = 3, columnIndex = 5 => boardColumnIndex = 8 (column I)
     * 
     * @return
     */
    public int getBoardColumnIndex()
    {
        return this.columnIndex + this.rowIndex;
    }

    /**
     * Returns the Slot Position in the board format (ex : "B1", "C2", etc)
     * 
     * @return
     */
    public String getBoardPositionString() throws IndexOutOfBoundsException
    {
        String columnLetter = "";

        switch (this.getBoardColumnIndex())
        {
            case 0:
                columnLetter = "A";
                break;
            case 1:
                columnLetter = "B";
                break;
            case 2:
                columnLetter = "C";
                break;
            case 3:
                columnLetter = "D";
                break;
            case 4:
                columnLetter = "E";
                break;
            case 5:
                columnLetter = "F";
                break;
            case 6:
                columnLetter = "G";
                break;
            case 7:
                columnLetter = "H";
                break;
            case 8:
                columnLetter = "I";
                break;
            case 9:
                columnLetter = "J";
                break;
            case 10:
                columnLetter = "K";
                break;
            case 11:
                columnLetter = "L";
                break;
            case 12:
                columnLetter = "M";
                break;
            default:  
                throw new IndexOutOfBoundsException("index " + this.getBoardColumnIndex() + " does not exists");
        }
        
        return columnLetter + String.valueOf(this.rowIndex + 1);
    }

    public int getRowIndex()
    {
        return rowIndex;
    }

    public int getColumnIndex()
    {
        return columnIndex;
    }
}
